Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

include: devicetree: Add port and endpoint macros #80649

Merged
merged 3 commits into from
Dec 6, 2024

Conversation

ngphibang
Copy link
Contributor

@ngphibang ngphibang commented Oct 30, 2024

This PR adds some port / endpoint DT helpers for the video drivers or others (e.g. display) and some examples of their simple usage in video drivers. Two main macros helpers are

  • DT_INST_ENDPOINT_BY_ID(inst, pid, id) : to get the (local) endpoint node id from a port id and an endpoint id
  • DT_NODE_REMOTE_DEVICE(ep) : to get the peer remote device node from the local endpoint node

A typical usage would be parsing all endpoints in a loop, or automatically parsing all endpoints till the end.

Copy link
Collaborator

@josuah josuah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks typical to have both DT_XXX(inst) and DT_INST_XXX(inst) variants defined, but not sure if the non-INST_ would ever be used.

#define DT_INST_XXX(inst) DT_XXX(DT_DRV_INST(inst))

Glad to offer the commit/review cycles it takes to get this ready for <zephyr/drivers/video.h> or <zephyr/devicetree.h>.

drivers/video/video_mcux_csi.c Outdated Show resolved Hide resolved
drivers/video/video_mcux_mipi_csi2rx.c Show resolved Hide resolved
Comment on lines 211 to 212
#define _DT_INST_PORT_BY_ID(n, pid) \
COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(n, ports)), (DT_CHILD(DT_INST_CHILD(n, ports), port_##pid)), (DT_INST_CHILD(n, port_##pid)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like an ongoing discussion: #58007
But the status quo seems still be on having Z_ for internal macros for now:

#define Z_IS_ENABLED1(config_macro) Z_IS_ENABLED2(_XXXX##config_macro)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I'm missing something here, why not use this pattern? Or remove the underscore if users should be using this macro directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use this pattern directly if we are sure that the target port has an ID, e.g. port_0, port_1, etc. It will not work in case we have only 1 port with no ID. I will remove the underscore as this macro can be used both internally and publicly.

include/zephyr/drivers/video.h Outdated Show resolved Hide resolved
include/zephyr/drivers/video.h Outdated Show resolved Hide resolved
include/zephyr/drivers/video.h Outdated Show resolved Hide resolved
include/zephyr/drivers/video.h Outdated Show resolved Hide resolved
@josuah
Copy link
Collaborator

josuah commented Nov 2, 2024

I tried to summarize my feedback into something like this, which I hope still matches what you had in mind:

/* Handle the variability of "ports{port@0{}};" vs "port{};" while going down */
#define DT_INST_PORT_BY_ID(inst, pid)                                                              \
	COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, ports)),                                    \
		    (DT_CHILD(DT_INST_CHILD(inst, ports), port_##pid)),                            \
		    (DT_INST_CHILD(inst, port)))

/* Handle the variability of "endpoint@0{};" vs "endpoint{};" while going down */
#define DT_INST_ENDPOINT_BY_ID(inst, pid, eid)                                                     \
	COND_CODE_1(DT_NODE_EXISTS(DT_CHILD(DT_INST_PORT_BY_ID(inst, pid), endpoint)),             \
		    (DT_CHILD(DT_INST_PORT_BY_ID(inst, pid), endpoint)),                           \
		    (DT_CHILD(DT_INST_PORT_BY_ID(inst, pid), endpoint_##eid)))

/* Handle the variability of "ports{port@0{}};" vs "port{};" while going up */
#define DT_ENDPOINT_PARENT_DEVICE(node)                                                            \
	COND_CODE_1(DT_NODE_EXISTS(DT_CHILD(DT_GPARENT(node), port)),                              \
		    (DT_GPARENT(node)), (DT_PARENT(DT_GPARENT(node))))

/* Handle the "remote-endpoint-label" */
#define DEVICE_DT_GET_REMOTE_DEVICE(node)                                                          \
	DEVICE_DT_GET(DT_ENDPOINT_PARENT_DEVICE(                                                   \
		DT_NODELABEL(DT_STRING_TOKEN(node, remote_endpoint_label))))

Feel free to cherry-pick from it or ignore it altogether.

@ngphibang
Copy link
Contributor Author

ngphibang commented Nov 5, 2024

/* Handle the variability of "ports{port@0{}};" vs "port{};" while going down */
#define DT_INST_PORT_BY_ID(inst, pid)                                                              \
	COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, ports)),                                    \
		    (DT_CHILD(DT_INST_CHILD(inst, ports), port_##pid)),                            \
		    (DT_INST_CHILD(inst, port)))

=> This does not work when there is no ports node but we still have multiple ports, each has a pid (they are not grouped under a ports node though). Note that the ports node is optional.

That's why I defined two macros so that it works for all cases.

/* Drivers should not use this except they know exactly that port has a pid */
#define _DT_INST_PORT_BY_ID(n, pid)                                                                \
	COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(n, ports)), (DT_CHILD(DT_INST_CHILD(n, ports), port_##pid)), (DT_INST_CHILD(n, port_##pid)))

#define DT_INST_PORT_BY_ID(n, pid)                                                                 \
	COND_CODE_1(DT_NODE_EXISTS(_DT_INST_PORT_BY_ID(n, pid)), (_DT_INST_PORT_BY_ID(n, pid)), (DT_INST_CHILD(n, port)))

Also note that the 1st macro is not only an intermediate / internal macro. It can be used by drivers as well if they know exactly that the target port has a pid.

Same comment for the endpoint macros.

@josuah
Copy link
Collaborator

josuah commented Nov 5, 2024

=> This does not work when there is no ports node but we still have multiple ports, each has a pid (they are not grouped under a ports node though). Note that the ports node is optional.

I had no idea! This all makes sense then.

josuah
josuah previously approved these changes Nov 5, 2024
Copy link
Collaborator

@josuah josuah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good for me! Thank you for your explanations on this.
+1 assuming that:

  • Indentation is tuned to get check_compliance.py happy
  • Documentation is added (glad to contribute it, but I do not want to break your workflow)
  • The naming matches what devicetree maintainers want
  • Their introduction in video.h is ok as per devicetree maintainers

Should the devicetree tag be added?

@ngphibang
Copy link
Contributor Author

ngphibang commented Nov 5, 2024

Relating to the remote endpoint and remote device macros:


> /* Handle the variability of "ports{port@0{}};" vs "port{};" while going up */
> #define DT_ENDPOINT_PARENT_DEVICE(node)                                                            \
> 	COND_CODE_1(DT_NODE_EXISTS(DT_CHILD(DT_GPARENT(node), port)),                              \
> 		    (DT_GPARENT(node)), (DT_PARENT(DT_GPARENT(node))))
> 
> /* Handle the "remote-endpoint-label" */
> #define DEVICE_DT_GET_REMOTE_DEVICE(node)                                                          \
> 	DEVICE_DT_GET(DT_ENDPOINT_PARENT_DEVICE(                                                   \
> 		DT_NODELABEL(DT_STRING_TOKEN(node, remote_endpoint_label))))

=> You check whether or not we have a port node (with no id) while we need to check rather on ports node because this will also break when we have multiple ports (with id) but no ports node.

However, by moving the node exist check to the local endpoint of the remote device, this version is simpler / better than mine. Will change to this. Thanks !

@ngphibang
Copy link
Contributor Author

ngphibang commented Nov 5, 2024

  • Their introduction in video.h is ok as per devicetree maintainers.

This is to be discussed. Ideally, it should be in devicetree.h / device.h. But remote-endpoint-label is not recognized system wide except in video-interfaces binding. Although we can declare this property in any dts without breaking the build but we cannot use the macros in code without having the corresponding binding. So, to introduce these macros in devicetree.h / device.h, we need to have also remote-endpoint-label in Zephyr generic bindings (base.yaml ?). How ?

If we keep them only for video, it's ok for the 1st phase. But do you know if there are examples in Zephyr that subsystem can have their own DT macros ? or all DT macros must go to devicetree.h ?

@josuah
Copy link
Collaborator

josuah commented Nov 5, 2024

It looks like driver classes do have their own macros! In a subdirectory of include/zephyr/devicetree.

For instance include/zephyr/devicetree/video.h file with macro named like DT_VIDEO_... or DT_INST_VIDEO_... if following the same pattern.

/* have these last so they have access to all previously defined macros */
#include <zephyr/devicetree/io-channels.h>
#include <zephyr/devicetree/clocks.h>
#include <zephyr/devicetree/gpio.h>
#include <zephyr/devicetree/spi.h>
#include <zephyr/devicetree/dma.h>
#include <zephyr/devicetree/pwms.h>
#include <zephyr/devicetree/fixed-partitions.h>
#include <zephyr/devicetree/ordinals.h>
#include <zephyr/devicetree/pinctrl.h>
#include <zephyr/devicetree/can.h>
#include <zephyr/devicetree/reset.h>
#include <zephyr/devicetree/mbox.h>

When the macros generate a struct specific to one subsystem (i.e. i2c_dt_spec), they are put in the include/zephyr/drivers/<class>.h with a name typically like <CLASS>_DT_...:

#define I2C_DT_SPEC_GET_ON_I3C(node_id) \
.bus = DEVICE_DT_GET(DT_BUS(node_id)), \
.addr = DT_PROP_BY_IDX(node_id, reg, 0)

Copy link
Collaborator

@danieldegrasse danieldegrasse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General comment- we need additional documentation on these macros before we can move forwards with this PR, I assume that will be added?

@ngphibang
Copy link
Contributor Author

General comment- we need additional documentation on these macros before we can move forwards with this PR, I assume that will be added?

Yes, I will put documentation and other things as discussed in the next push.

@josuah josuah self-requested a review December 5, 2024 16:55
danieldegrasse
danieldegrasse previously approved these changes Dec 5, 2024
Copy link
Collaborator

@danieldegrasse danieldegrasse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine to me, thank you for this update

@ngphibang
Copy link
Contributor Author

Looks fine to me, thank you for this update

I still need to resolve @rruuaanng 's comments, will do soon :-).

@ngphibang
Copy link
Contributor Author

ngphibang commented Dec 5, 2024

@rruuaanng : In fact, there was already a blank line at the end of the file in my local IDE but github does not show it. I tried to add one more to the files as your request but then check-compliance has failed as you can see in CI check:

ERROR   : Test YAMLLint failed: 
YAMLLint (empty-lines):too many blank lines (1 > 0)
File:dts/bindings/test/vnd,video-multi-port.yaml
Line:15
Column:1
ERROR   : Test YAMLLint failed: 
YAMLLint (empty-lines):too many blank lines (1 > 0)
File:dts/bindings/test/vnd,video-single-port.yaml
Line:14
Column:1
ERROR   : Test Nits failed: 
Please remove blank lines at end of 'dts/bindings/test/vnd,video-multi-port.yaml'
ERROR   : Test Nits failed: 
Please remove blank lines at end of 'dts/bindings/test/vnd,video-single-port.yaml'
ERROR   : Test GitDiffCheck failed: 
0e805944c8829c236348b4dcdd41e4f9fb8eb6a0: dts/bindings/test/vnd,video-multi-port.yaml:15: new blank line at EOF.
0e805944c8829c236348b4dcdd41e4f9fb8eb6a0: dts/bindings/test/vnd,video-single-port.yaml:14: new blank line at EOF.

So, I will let it as before then ?

@ngphibang ngphibang requested a review from rruuaanng December 5, 2024 22:00
josuah
josuah previously approved these changes Dec 5, 2024
@rruuaanng
Copy link
Collaborator

rruuaanng commented Dec 6, 2024

@rruuaanng : In fact, there was already a blank line at the end of the file in my local IDE but github does not show it. I tried to add one more to the files as your request but then check-compliance has failed as you can see in CI check:


ERROR   : Test YAMLLint failed: 

YAMLLint (empty-lines):too many blank lines (1 > 0)

File:dts/bindings/test/vnd,video-multi-port.yaml

Line:15

Column:1

ERROR   : Test YAMLLint failed: 

YAMLLint (empty-lines):too many blank lines (1 > 0)

File:dts/bindings/test/vnd,video-single-port.yaml

Line:14

Column:1

ERROR   : Test Nits failed: 

Please remove blank lines at end of 'dts/bindings/test/vnd,video-multi-port.yaml'

ERROR   : Test Nits failed: 

Please remove blank lines at end of 'dts/bindings/test/vnd,video-single-port.yaml'

ERROR   : Test GitDiffCheck failed: 

0e805944c8829c236348b4dcdd41e4f9fb8eb6a0: dts/bindings/test/vnd,video-multi-port.yaml:15: new blank line at EOF.

0e805944c8829c236348b4dcdd41e4f9fb8eb6a0: dts/bindings/test/vnd,video-single-port.yaml:14: new blank line at EOF.

So, I will let it as before then ?

It seems that my comment is not so clear. If there is a blank line at the end of the file, you can ignore my comment. I just suggest that please forgive my unclear description.

Edit

Delete the extra blank line and keep only one blank line at the end of the file. I apologize again :(

@ngphibang
Copy link
Contributor Author

@rruuaanng No problem :-). Done.

ngphibang and others added 3 commits December 6, 2024 11:06
Add port and endpoint DT macros to retrieve the node id of the interested
port/endpoint from its id. Also, add helpers to retrieve the peer remote
device node from its local endpoint interface.

Signed-off-by: Phi Bang Nguyen <[email protected]>
Co-developed-by: Josuah Demangeon <[email protected]>
Add tests for the new port / endpoint DT macros

Signed-off-by: Josuah Demangeon <[email protected]>
Signed-off-by: Phi Bang Nguyen <[email protected]>
Drop the driver-defined macros to use the endpoint DT helpers instead.

Signed-off-by: Phi Bang Nguyen <[email protected]>
@ngphibang ngphibang force-pushed the add_endpoint_parser branch from 9b4b9a7 to 1ea737e Compare December 6, 2024 10:07
@ngphibang
Copy link
Contributor Author

force-push to fix CI build issue due to #79482 (which take these macros inside it) get merged first

@ngphibang
Copy link
Contributor Author

@danieldegrasse Could you revisit ?

Copy link
Collaborator

@danieldegrasse danieldegrasse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@decsny could you take a look as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Devicetree area: Video Video subsystem platform: NXP Drivers NXP Semiconductors, drivers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants